package il.ac.tau.cg.ex3.parser.parametershander.surfaces;

import il.ac.tau.cg.ex3.entities.Entity;
import il.ac.tau.cg.ex3.entities.styles.Color;
import il.ac.tau.cg.ex3.entities.styles.Material;
import il.ac.tau.cg.ex3.entities.styles.drawables.CheckersDrawable;
import il.ac.tau.cg.ex3.entities.styles.drawables.ColorDrawable;
import il.ac.tau.cg.ex3.entities.styles.drawables.Drawable;
import il.ac.tau.cg.ex3.entities.styles.drawables.ImageDrawable;
import il.ac.tau.cg.ex3.parser.Parser;
import il.ac.tau.cg.ex3.parser.Parser.ParseException;
import il.ac.tau.cg.ex3.parser.parametershander.EntityParametersHandler;

import java.awt.image.BufferedImage;

public abstract class SurfaceParameterHandler extends EntityParametersHandler
{
	private String materialType;
	private Color diffuse;
	private Drawable drawable;
	private Color specular; 
	private Color ambient;
	private Color emission;
	private double shininess;
	private double checkerSize;
	private Color checkerDiffuse1;
	private Color checkerDiffuse2;
	private BufferedImage texImage;
	private double reflectance;
	
	public SurfaceParameterHandler()
	{
		super();
		
		materialType     = "flat";
		diffuse          = new Color(0.8, 0.8, 0.8);
		drawable         = null;
		specular         = new Color(); 
		ambient          = new Color(0.1, 0.1, 0.1);
		emission         = new Color(0.0, 0.0, 0.0);
		shininess        = 100;
		checkerSize      = 0.1;
		checkerDiffuse1  = new Color(1.0, 1.0, 1.0);
		checkerDiffuse2  = new Color(0.1, 0.1, 0.1);
		texImage         = null;
		reflectance      = 0.0;
	}
	
	public boolean addParam(String key, String[] value) throws ParseException
	{
		paramsCache.add(key);
		
		ESurfaceParameters param =  ESurfaceParameters.findParam(key);
		
		if (param == null)
		{
			return false;
		}
		
		Parser.pAssert((value.length == param.argsNum), param.name + " INVALID ARGS NUM");
			
		switch (param)
		{
			case MTL_TYPE:
			{
				materialType = value[0];
			}break;
				
			case MTL_DIFFUSE:
			{
				diffuse = getColor(value);
			}break;
			
			case MTL_AMBIENT:
			{
				ambient = getColor(value);
			}break;
			
			case MTL_EMISSION:
			{
				emission = getColor(value);
			}break;
			
			case MTL_SHININESS:
			{
				shininess = Double.parseDouble(value[0]);
			}break;
			
			case MTL_SPECULAR:
			{
				specular = getColor(value);
			}break;
			
			case CHECKERS_DIFFUSE1:
			{
				checkerDiffuse1 = getColor(value);
			}break;
			
			case CHECKERS_DIFFUSE2:
			{
				checkerDiffuse2 = getColor(value);
			}break;
			
			case CHECKERS_SIZE:
			{
				checkerSize = Double.parseDouble(value[0]);
			}break;
			
			case TEXTURE:
			{
				texImage = getTexture(value);
			}break;
			
			case REFLECTANCE:
			{
				reflectance = Double.parseDouble(value[0]);
			}break;
		}
		
		return true;
	}
	
	protected Entity createEntity() throws ParseException
	{
		if (materialType.equals("flat"))
		{
			drawable = new ColorDrawable(diffuse);
		}
		else if (materialType.equals("checkers"))
		{
			drawable = new CheckersDrawable(checkerSize, checkerDiffuse1, checkerDiffuse2);
		}
		else if (materialType.equals("texture"))
		{
			drawable = new ImageDrawable(texImage);
		}
		else
		{
			Parser.pAssert(false, "UNSUPPORTED TYPE");
		}
		
		return new Material(drawable, specular, ambient, emission, shininess, reflectance);
	}

	@Override
	protected boolean validateEntity() throws ParseException
	{
		for (ESurfaceParameters param : ESurfaceParameters.values())
		{
			if (param.isMandatory && !paramsCache.contains(param.name))
			{
				return false;
			}
		}
		
		return true;
	}
	
	public enum ESurfaceParameters
	{
		/* SURFACES ENTITIES */
		MTL_TYPE          ("mtl-type", false, 1),
		MTL_DIFFUSE       ("mtl-diffuse", false, 3),
		MTL_SPECULAR      ("mtl-specular", false, 3),
		MTL_AMBIENT       ("mtl-ambient", false, 3),
		MTL_EMISSION      ("mtl-emission", false, 3),
		MTL_SHININESS     ("mtl-shininess", false, 1),
		TEXTURE           ("texture", false, 1),
		CHECKERS_SIZE     ("checkers-size", false, 1),
		CHECKERS_DIFFUSE1 ("checkers-diffuse1", false, 3),
		CHECKERS_DIFFUSE2 ("checkers-diffuse2", false, 3),
		REFLECTANCE       ("reflectance", false, 1);
		
		private final String  name;
		private final boolean isMandatory;
		private final int     argsNum;
		
		ESurfaceParameters(String name, boolean isMandatory, int argsNum)
		{
			this.name = name;
			this.isMandatory = isMandatory;
			this.argsNum = argsNum;
		}
		
		public static ESurfaceParameters findParam(String name)
		{
			for (ESurfaceParameters param : ESurfaceParameters.values())
			{
				if (param.name.equals(name))
				{
					return param;
				}
			}
			
			return null;
		}
	}
}

